package com.example.Arithmetic.Arithmetic;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 日期：2023/10/11
 * 时间：9:10
 * 描述：单锁实现线程安全的队列
 * 写入和移除不可同时操作，size有不可重复读风险
 */
public class BlockingQueueMonoLock<E> implements com.example.Arithmetic.Arithmetic.Port.BlockingQueue<E> {

    E[] array;
    int head;
    int tail;
    int size;

    public BlockingQueueMonoLock(int capacity) {
        array = (E[]) new Object[capacity];
    }

    ReentrantLock reentrantLock = new ReentrantLock();
    Condition HeadWaits = reentrantLock.newCondition();
    Condition TailWaits = reentrantLock.newCondition();

    @Override
    public void offer(E e) throws InterruptedException {
        reentrantLock.lockInterruptibly();
        try {
            while (isFull()) {
                TailWaits.await();
            }
            size++;
            array[tail] = e;
            if (++tail == array.length) {
                tail = 0;
            }
            HeadWaits.signal();          //通知移除方法锁有数据了
        } finally {
            reentrantLock.unlock();
        }
    }

    @Override
    public boolean offer(E e, long timeout) throws InterruptedException {
        reentrantLock.lockInterruptibly();
        long l = TimeUnit.MILLISECONDS.toNanos(timeout);
        try {
            while (isFull()) {
                if (l <= 0) {
                    return false;
                }
                l = TailWaits.awaitNanos(l);
            }
            array[tail] = e;
            if (++tail == array.length) {
                tail = 0;
            }
            size++;
            HeadWaits.signal();
        } finally {
            TailWaits.signal();
        }
        return true;
    }

    @Override
    public E poll() throws InterruptedException {
        reentrantLock.lockInterruptibly();
        try {
            while (isEmpty()) {
                HeadWaits.await();
            }
            E e = array[head];
            array[head] = null;   //help GC
            if (++head == array.length) {
                head = 0;
            }
            size--;
            TailWaits.signal();            //通知新增方法有空位了
            return e;
        } finally {
            reentrantLock.unlock();
        }
    }

    private boolean isEmpty() {
        return size == 0;
    }

    private boolean isFull() {
        return size == array.length;
    }
}
